package tt.dz.order.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.net.ssl.SSLContext;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import tt.dz.order.dao.SysApplicationContextAware;

/**
 * @title 微信支付工具类(退款相关业务使用,后续开发可考虑清理)
 * @author 倪波
 */
@Component
public class WeiXinPayUtil {
	private static Logger log = LoggerFactory.getLogger(WeiXinPayUtil.class);

	public static final String RET_SUCCESS_XML = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";

	public static final String RET_FAIL_XML = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[FAIL]]></return_msg></xml>";

	public static final String RET_SUCCESS_STR = "SUCCESS";

	public static final String RET_FAIL_STR = "FAIL";

	/** 微信商户号 */
	@Value(value = "${weixin.mchId}")
	public String MCH_ID;

	/** api密钥 */
	@Value(value = "${weixin.mchId}")
	private String APIKey;

	/** 商户证书文件路径 */
	@Value(value = "${weixin.mchPcks12FilePath}")
	private String MCH_PKCS12_FILEPATH;

	/** 微信支付退款接口地址 */
	private static final String WEIXIN_REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";

	public static final String INPUT_CHARSET = "utf-8";

	/** 数据传输超时时间，默认为2秒钟 */
	private static int socketTimeout = 5 * 1000;

	/** 连接超时时间，默认为8秒钟 */
	private static int connectionTimeout = 8 * 1000;

	private static SSLConnectionSocketFactory sslsf;

	/*
	 * static { // 微信商户号 MCH_ID =
	 * SpringPropertyResourceReader.getProperty("weixin.mchId"); // api密钥 APIKey
	 * = SpringPropertyResourceReader.getProperty("weixin.apiKey"); // 商户证书文件路径
	 * MCH_PKCS12_FILEPATH =
	 * SpringPropertyResourceReader.getProperty("weixin.mchPcks12FilePath"); }
	 */

	@PostConstruct
	public void init() {
		// 退款流程，需要加载证书，当前不需要退款流程
		FileInputStream instream = null;
		try {
			KeyStore keyStore = KeyStore.getInstance("PKCS12");
			instream = new FileInputStream(new File(MCH_PKCS12_FILEPATH));
			keyStore.load(instream, MCH_ID.toCharArray());
			// Trust own CA and all self-signed certs
			SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, MCH_ID.toCharArray()).build();
			// Allow TLSv1 protocol only
			sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
					SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
		} catch (Exception e) {
			log.error("微信支付初始化加载KeyStore失败，" + e);
		} finally {
			if (instream != null) {
				try {
					instream.close();
				} catch (IOException e) {
					log.error("微信支付初始化加载KeyStore，关闭instream出错" + e);
				}
			}
		}
	}

	/**
	 * 除去数组中的空值和签名参数
	 * 
	 * @param sArray
	 *            签名参数组
	 * @return 去掉空值与签名参数后的新签名参数组
	 */
	public static Map<String, String> paraFilter(Map<String, Object> sArray) {
		Map<String, String> result = new HashMap<String, String>();
		if (sArray == null || sArray.size() <= 0) {
			return result;
		}
		for (String key : sArray.keySet()) {
			String value = (String) sArray.get(key);
			if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
					|| key.equalsIgnoreCase("sign_type")) {
				// 值为空或者key为"sign"的参数不进行拼接
				continue;
			}
			result.put(key, value);
		}
		return result;
	}

	/**
	 * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
	 * 
	 * @param params
	 *            需要排序并参与字符拼接的参数组
	 * @return 拼接后字符串
	 */
	public static String createLinkString(Map<String, String> params) {
		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);
		StringBuilder prestr = new StringBuilder();
		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);
			if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
				prestr = prestr.append(key).append("=").append(value);
			} else {
				prestr = prestr.append(key).append("=").append(value).append("&");
			}
		}
		return prestr.toString();
	}

	/**
	 * 微信支付回调验证sign签名
	 * 
	 * @param params
	 * @return true/false
	 * @throws NoSuchAlgorithmException
	 * @throws UnsupportedEncodingException
	 */
	public static boolean verify(Map<String, Object> params)
			throws NoSuchAlgorithmException, UnsupportedEncodingException {
		String sign = (String) params.get("sign");
		if (sign == null) {
			log.error("微信支付回调验证sign签名,签名为空");
			return false;
		}
		// 过滤空值、sign与sign_type参数
		Map<String, String> sParaNew = paraFilter(params);
		// 拼接字符串
		String linkedStr = createLinkString(sParaNew);
		String preSignStr = new StringBuilder(linkedStr).append("&key=")
				.append(SysApplicationContextAware.getBean(WeiXinPayUtil.class).APIKey).toString();
		String signedStr = DigestUtils.md5Hex(preSignStr.getBytes(INPUT_CHARSET)).toUpperCase();
		return sign.equals(signedStr);
	}

	/**
	 * 微信支付生成sign签名
	 * 
	 * @return 签名字符串
	 */
	public static String sign(Map<String, Object> params) throws UnsupportedEncodingException {
		// 过滤空值、sign与sign_type参数
		Map<String, String> sParaNew = paraFilter(params);
		// 拼接字符串
		String linkedStr = createLinkString(sParaNew);
		String preSignStr = new StringBuilder(linkedStr).append("&key=")
				.append(SysApplicationContextAware.getBean(WeiXinPayUtil.class).APIKey).toString();
		String signedStr = DigestUtils.md5Hex(preSignStr.getBytes(INPUT_CHARSET)).toUpperCase();
		return signedStr;
	}

	/**
	 * 微信支付退款发送https请求
	 * 
	 * @param params
	 */
	public static String sendRefundRequest(String paramXmlStr) {
		StringBuilder retXmlStr = new StringBuilder();
		CloseableHttpClient httpclient = null;
		CloseableHttpResponse response = null;
		try {
			httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
			HttpPost httpPost = new HttpPost(WEIXIN_REFUND_URL);
			// 设置请求和传输超时时间httpGet.setConfig(requestConfig);
			RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout)
					.setConnectTimeout(connectionTimeout).build();
			httpPost.setConfig(requestConfig);
			StringEntity strEntity = new StringEntity(paramXmlStr,
					ContentType.create("application/xml", INPUT_CHARSET));
			httpPost.setEntity(strEntity);
			response = httpclient.execute(httpPost);
			int responseStatus = response.getStatusLine().getStatusCode();
			if (responseStatus != HttpStatus.SC_OK) {
				log.error("微信支付退款发送https请求，响应状态不正确，responseStatus：" + responseStatus);
				return retXmlStr.toString();
			}
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				InputStreamReader isr = new InputStreamReader(entity.getContent(), INPUT_CHARSET);
				BufferedReader bufferedReader = new BufferedReader(isr);
				String text;
				while ((text = bufferedReader.readLine()) != null) {
					retXmlStr.append(text);
				}
				bufferedReader.close();
				isr.close();
			}
			EntityUtils.consume(entity);
		} catch (Exception e) {
			log.error("微信支付退款发送https请求出错，" + e);
		} finally {
			try {
				if (response != null) {
					response.close();
				}
				if (httpclient != null) {
					httpclient.close();
				}
			} catch (IOException e) {
				log.error("微信支付退款发送https请求,关闭httpclient出错" + e);
			}
		}
		return retXmlStr.toString();
	}
}